/*
 * JsonUtil.h
 *
 *  Created on: 2018年3月4日
 *      Author: chuanjiang.zh@qq.com
 */

#ifndef JSONUTIL_H_
#define JSONUTIL_H_

#include "BasicType.h"
#include "json/json.h"
#include <memory>
#include <sstream>
#include <functional>

typedef std::shared_ptr<Json::StreamWriter>  StreamWriterPtr;



namespace Json
{

    typedef std::function< bool(const Json::Value& json) > JsonPredicate;

	template < class T>
    bool parseJson(const Json::Value& json, T& t);
	//{
	//	return false;
	//}

	template < class T>
    void toJson(const T& t, Json::Value& value);
	//{
 //       //
 //       printf("toJson null\n");
	//}

    template < class T>
    void intoJson(const T& t, Json::Value& value, const char* name)
    {
        Json::Value item;
        toJson(t, item);
        value[name] = item;
    }

    template < class Cont>
    bool fromArray(const Json::Value& value, Cont& cont)
    {
        if (!value.isArray())
        {
            return false;
        }

        for (Json::ArrayIndex i = 0; i < value.size(); ++i)
        {
            Json::Value item = value[i];
            typename Cont::value_type element;
            if (Json::parseJson(item, element))
            {
                cont.push_back(element);
            }
        }
        return true;
    }

    template < class Cont>
    bool fromArray(const Json::Value& value, Cont& cont, JsonPredicate pred)
    {
        if (!value.isArray())
        {
            return false;
        }

        for (Json::ArrayIndex i = 0; i < value.size(); ++i)
        {
            Json::Value item = value[i];
            if (!pred(item))
            {
                continue;
            }

            typename Cont::value_type element;
            if (Json::parseJson(item, element))
            {
                cont.push_back(element);
            }
        }
        return true;
    }

    template < class T>
    size_t fromArray(const Json::Value& value, T* cont, size_t maxsize)
    {
        if (!value.isArray())
        {
            return 0;
        }

        size_t count = 0;
        for (Json::ArrayIndex i = 0; i < value.size() && count < maxsize; ++i)
        {
            Json::Value item = value[i];
            if (Json::parseJson(item, cont[count]))
            {
                count ++;
            }
        }
        return count;
    }


    template < class Cont >
    void toList(Cont& cont, std::vector< Json::Value >& values)
    {
        typename Cont::iterator it = cont.begin();
        for (; it != cont.end(); ++it)
        {
            typename Cont::value_type& v = *it;
            Json::Value element;
            Json::toJson(v, element);
            values.push_back(element);
        }
    }


    template < class Cont >
    void toArray(const Cont& cont, Json::Value& value)
    {
        value = Json::Value(Json::arrayValue);
        typename Cont::const_iterator it = cont.begin();
        for (; it != cont.end(); ++it)
        {
            Json::Value element;
            Json::toJson(*it, element);
            value.append(element);
        }
    }

    template < class Cont, class Proc >
    void toArray(const Cont& cont, Json::Value& value, Proc proc)
    {
        value = Json::Value(Json::arrayValue);
        typename Cont::const_iterator it = cont.begin();
        for (; it != cont.end(); ++it)
        {
            Json::Value element;
            Json::toJson(*it, element);

            proc(*it, element);

            value.append(element);
        }
    }

    template < class Cont >
    void pointersToArray(Cont& cont, Json::Value& value)
    {
        value = Json::Value(Json::arrayValue);
        typename Cont::iterator it = cont.begin();
        for (; it != cont.end(); ++it)
        {
            typename Cont::value_type& v = *it;
            Json::Value element;
            Json::toJson(*v, element);
            value.append(element);
        }
    }

    template < class Cont >
    void basicToArray(const Cont& cont, Json::Value& value)
    {
        value = Json::Value(Json::arrayValue);
        typename Cont::const_iterator it = cont.begin();
        for (; it != cont.end(); ++it)
        {
            Json::Value element = *it;
            value.append(element);
        }
    }


    template < class T >
    std::string toString(const T& t)
    {
        Json::Value value;
        Json::toJson(t, value);
        return value.toStyledString();
    }


	enum FormatFlag
	{
		kNone = 0,
		kIgnoreNull  = 0x01,
		kQuoteString = 0x02
	};

    

	template <class JsonList>
	std::string listToString(const JsonList& jsonList)
	{
		std::ostringstream sout;
		Json::StreamWriterBuilder builder;
		StreamWriterPtr const writer(builder.newStreamWriter());

		sout << "[";

		int idx = 0;
		typename JsonList::const_iterator it = jsonList.begin();
		for (; it != jsonList.end(); ++ it)
		{
			if (idx > 0)
			{
				sout << ",";
			}

			writer->write(*it, &sout);
			idx ++;
		}

		sout << "]";

		return sout.str();
	}

	std::ostream& write(std::ostream& os, const Json::Value& json, char quote);

	std::string joinKey(const Json::Value& json, const std::string& delimiter,
				char quote,
				const std::string& prefix, const std::string& postfix);

	std::string joinKey(const Json::Value& json, const std::string& delimiter,
			char quote,
			const std::string& prefix, const std::string& postfix, int flags);

	std::string joinValue(const Json::Value& json, const std::string& delimiter,
			const std::string& prefix, const std::string& postfix, int flags);

	std::string join(const Json::Value& json, const std::string& equalSign, const std::string& delimiter,
			char quote,
			const std::string& prefix, const std::string& postfix, int flags);

    void setIfNot(Json::Value& json, const std::string& key, const std::string& value);
    void setIfNot(Json::Value& json, const std::string& key, int value);
    void setIfNot(Json::Value& json, const std::string& key, double value);

    bool optGet(const Json::Value& json, const char* name, int& value);
	bool optGet(const Json::Value& json, const char* name, double& value);
	bool optGet(const Json::Value& json, const char* name, float& value);
	bool optGet(const Json::Value& json, const char* name, std::string& value);
	bool optGet(const Json::Value& json, const char* name, bool& value);
	bool optGet(const Json::Value& json, const char* name, int64_t& value);
	bool optGet(const Json::Value& json, const char* name, char* value, size_t maxsize);

	template <class T>
	bool optGetStr(const Json::Value& json, const char* name, T& dest)
	{
		return optGet(json, name, (char*)dest, sizeof(dest));
	}

    bool optGetArray(const Json::Value& json, const char* name, Json::Value& child);

	bool optGetToString(const Json::Value& json, const char* name, std::string& str);

    bool optGetBool(const Json::Value& json, const char* name, int& dest);


	bool load(const std::string& filepath, Json::Value& value);
	bool save(const Json::Value& value, const std::string& filepath);

	bool copy(const Json::Value& src, const std::string& key, Json::Value& dest);

	bool parse(const char* str, size_t len, Json::Value& value);
	bool parse(const std::string& str, Json::Value& value);

	std::string toCompact(const Json::Value& value);

	void join(Json::Value& dest, const Json::Value& src);

    bool isArray(const Json::Value& value, const char* key);

    int toIntArray(const Json::Value& json, std::vector<int>& ids);
    int toIntArray(const Json::Value& json, std::vector<int64_t>& ids);
    int toStringArray(const Json::Value& json, std::vector<std::string>& ids);

}

#endif /* JSONUTIL_H_ */
